#include "stdafx.h"
#include "net/DNS.hpp"
#include "utils/numberformatter.hpp"
using JHCPP::utils::CNumberFormatter;

NET_NAMESPACE_BEGIN

CHostEntry CDNS::hostByName(const std::string& hostname, unsigned hintFlags)
{
	struct hostent* he = gethostbyname(hostname.c_str());
	if (he)
	{
		return CHostEntry(he);
	}

	error(lastError(), hostname); 
	throw NetException(); 
}

CHostEntry CDNS::hostByAddress(const CIPAddress& address, unsigned hintFlags)
{
	struct hostent* he = gethostbyaddr(reinterpret_cast<const char*>(address.addr()), address.length(), address.af());
	if (he)
	{
		return CHostEntry(he);
	}

	int err = lastError();
	error(err, address.toString()); 
	throw NetException();
}

CHostEntry CDNS::resolve(const std::string& address)
{
	CIPAddress ip;
	if (CIPAddress::tryParse(address, ip))
		return hostByAddress(ip);
	else
		return hostByName(address);
}

CIPAddress CDNS::resolveOne(const std::string& address)
{
	const CHostEntry& entry = resolve(address);
	if (!entry.addresses().empty())
		return entry.addresses()[0];
	else
		throw NoAddressFoundException(address);
}

CHostEntry CDNS::thisHost()
{
	return hostByName(hostName());
}

std::string CDNS::hostName()
{
	char buffer[256];
	int rc = gethostname(buffer, sizeof(buffer));
	if (rc == 0)
		return std::string(buffer);
	else
		throw NetException("Cannot get host name");
}

int CDNS::lastError()
{
#if defined(_WIN32)
	return ::GetLastError();
#else
	return h_errno;
#endif
}

void CDNS::error(int code, const std::string& arg)
{
	switch (code)
	{
	case JH_ESYSNOTREADY:
		throw NetException("Net subsystem not ready");
	case JH_ENOTINIT:
		throw NetException("Net subsystem not initialized");
	case JH_HOST_NOT_FOUND:
		throw HostNotFoundException(arg);
	case JH_TRY_AGAIN:
		throw DNSException("Temporary DNS error while resolving", arg);
	case JH_NO_RECOVERY:
		throw DNSException("Non recoverable DNS error while resolving", arg);
	case JH_NO_DATA:
		throw NoAddressFoundException(arg);
	default:
		throw IOException(CNumberFormatter::format(code));
	}
}

void CDNS::aierror(int code, const std::string& arg)
{
#if defined(JH_HAVE_IPv6)
	switch (code)
	{
	case EAI_AGAIN:
		throw DNSException("Temporary DNS error while resolving", arg);
	case EAI_FAIL:
		throw DNSException("Non recoverable DNS error while resolving", arg);
#if !defined(_WIN32) // EAI_NODATA and EAI_NONAME have the same value
#if defined(EAI_NODATA) // deprecated in favor of EAI_NONAME on FreeBSD
	case EAI_NODATA:
		throw NoAddressFoundException(arg);
#endif
#endif
	case EAI_NONAME:
		throw HostNotFoundException(arg);
#if defined(EAI_SYSTEM)
	case EAI_SYSTEM:
		error(lastError(), arg);
		break;
#endif
#if defined(_WIN32)
	case WSANO_DATA: // may happen on XP
		throw HostNotFoundException(arg);
#endif
	default:
		throw DNSException("EAI", CNumberFormatter::format(code));
	}
#endif 
}


NET_NAMESPACE_END
